{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-02-12T22:17:19.603440Z",
     "start_time": "2019-02-12T22:17:19.523397Z"
    }
   },
   "source": [
    "<h1><center> Facial Emotion Recognition - Simple Architecture </center></h1>\n",
    "<center> A project for the French Employment Agency </center>\n",
    "<center> Telecom ParisTech 2018-2019 </center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# I. Context"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The aim of this notebook is to explore facial emotion recognition techniques from a live webcam video stream. \n",
    "\n",
    "The data set used for training is the Kaggle FER2013 emotion recognition data set : https://www.kaggle.com/c/challenges-in-representation-learning-facial-expression-recognition-challenge/data\n",
    "\n",
    "The models explored include :\n",
    "- Manual filters \n",
    "- Deep Learning Architectures\n",
    "- DenseNet Inspired Architectures\n",
    "\n",
    "This model will be combined with voice emotion recongition as well as psychological traits extracted from text inputs, and should provide a benchmark and a deep analysis of both verbal and non-verbal insights for candidates seeking for a job and their performance during an interview."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# II. General imports"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Versions used :"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "Python : 3.6.5\n",
    "Tensorflow : 1.10.1\n",
    "Keras : 2.2.2\n",
    "Numpy : 1.15.4\n",
    "OpenCV : 4.0.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:03:02.158363Z",
     "start_time": "2019-04-11T07:02:54.878952Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/anaconda3/lib/python3.6/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n",
      "Using TensorFlow backend.\n",
      "/anaconda3/lib/python3.6/site-packages/requests/__init__.py:80: RequestsDependencyWarning: urllib3 (1.21.1) or chardet (2.3.0) doesn't match a supported version!\n",
      "  RequestsDependencyWarning)\n"
     ]
    }
   ],
   "source": [
    "### General imports ###\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from time import time\n",
    "from time import sleep\n",
    "import re\n",
    "import os\n",
    "import argparse\n",
    "from collections import OrderedDict\n",
    "import matplotlib.animation as animation\n",
    "\n",
    "### Image processing ###\n",
    "from scipy.ndimage import zoom\n",
    "from scipy.spatial import distance\n",
    "import imutils\n",
    "from scipy import ndimage\n",
    "import cv2\n",
    "import dlib\n",
    "from __future__ import division\n",
    "from imutils import face_utils\n",
    "\n",
    "### CNN models ###\n",
    "import keras\n",
    "from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img\n",
    "from keras.callbacks import TensorBoard\n",
    "from keras.models import Sequential\n",
    "from keras.layers.core import Dense, Dropout, Activation, Flatten\n",
    "from keras.layers.convolutional import Conv2D, MaxPooling2D, SeparableConv2D\n",
    "from keras.utils import np_utils\n",
    "from keras.regularizers import l2#, activity_l2\n",
    "from keras.optimizers import SGD, RMSprop\n",
    "from keras.utils import to_categorical\n",
    "from keras.layers.normalization import BatchNormalization\n",
    "from keras import models\n",
    "from keras.utils.vis_utils import plot_model\n",
    "from keras.layers import Input, GlobalAveragePooling2D\n",
    "from keras.models import Model\n",
    "from tensorflow.keras import layers\n",
    "\n",
    "### Build SVM models ###\n",
    "from sklearn.preprocessing import OneHotEncoder\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.metrics import accuracy_score\n",
    "from sklearn import svm\n",
    "\n",
    "### Same trained models ###\n",
    "import h5py\n",
    "from keras.models import model_from_json\n",
    "import pickle"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# III. Import datas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:03:02.165970Z",
     "start_time": "2019-04-11T07:03:02.161858Z"
    }
   },
   "outputs": [],
   "source": [
    "path = '/Users/maelfabien/filrouge_pole_emploi/Video/'\n",
    "local_path = '/Users/maelfabien/Desktop/LocalDB/Videos/'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:03:02.910881Z",
     "start_time": "2019-04-11T07:03:02.169212Z"
    }
   },
   "outputs": [],
   "source": [
    "X_train = np.load(local_path + \"X_train.npy\")\n",
    "X_test = np.load(local_path + \"X_test.npy\")\n",
    "y_train = np.load(local_path + \"y_train.npy\")\n",
    "y_test = np.load(local_path + \"y_test.npy\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:03:02.937172Z",
     "start_time": "2019-04-11T07:03:02.917677Z"
    }
   },
   "outputs": [],
   "source": [
    "shape_x = 48\n",
    "shape_y = 48\n",
    "nRows,nCols,nDims = X_train.shape[1:]\n",
    "input_shape = (nRows, nCols, nDims)\n",
    "classes = np.unique(y_train)\n",
    "nClasses = len(classes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:04:18.572219Z",
     "start_time": "2019-04-11T07:04:18.563832Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(28709, 7)"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_train.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:04:50.510624Z",
     "start_time": "2019-04-11T07:04:50.448088Z"
    }
   },
   "outputs": [],
   "source": [
    "class_weight = {\n",
    "    0:1/sum(y_train[:,0]), \n",
    "    1:sum(y_train[:,1]), \n",
    "    2:sum(y_train[:,2]), \n",
    "    3:sum(y_train[:,3]),\n",
    "    4:sum(y_train[:,4]),\n",
    "    5:sum(y_train[:,5]),\n",
    "    6:sum(y_train[:,6])\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# IV. Build the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:04:52.738571Z",
     "start_time": "2019-04-11T07:04:52.695556Z"
    }
   },
   "outputs": [],
   "source": [
    "def createModel():\n",
    "    model = Sequential()\n",
    "\n",
    "    model.add(Conv2D(64, (3, 3), padding='same', input_shape=(48,48,1)))\n",
    "    model.add(BatchNormalization())\n",
    "    model.add(Activation('relu'))\n",
    "    model.add(MaxPooling2D(pool_size=(2, 2), strides=None, padding='same'))\n",
    "    model.add(Dropout(0.25))\n",
    "\n",
    "    model.add(Conv2D(128, (3, 3), padding='same'))\n",
    "    model.add(BatchNormalization())\n",
    "    model.add(Activation('relu'))\n",
    "    model.add(MaxPooling2D(pool_size=(2, 2), strides=None, padding='same'))\n",
    "    model.add(Dropout(0.25))\n",
    "\n",
    "    model.add(Conv2D(256, (3, 3), padding='same'))\n",
    "    model.add(BatchNormalization())\n",
    "    model.add(Activation('relu'))\n",
    "    model.add(MaxPooling2D(pool_size=(2, 2), strides=None, padding='same'))\n",
    "    model.add(Dropout(0.25))\n",
    "\n",
    "    model.add(Conv2D(512, (3, 3), padding='same'))\n",
    "    model.add(BatchNormalization())\n",
    "    model.add(Activation('relu'))\n",
    "    model.add(MaxPooling2D(pool_size=(2, 2), strides=None, padding='same'))\n",
    "    model.add(Dropout(0.25))\n",
    "\n",
    "    model.add(Flatten())\n",
    "\n",
    "    model.add(Dense(512))\n",
    "    model.add(BatchNormalization())\n",
    "    model.add(Activation('relu'))\n",
    "    model.add(Dropout(0.25))\n",
    "\n",
    "    model.add(Dense(256))\n",
    "    model.add(BatchNormalization())\n",
    "    model.add(Activation('relu'))\n",
    "    model.add(Dropout(0.25))\n",
    "\n",
    "    model.add(Dense(7))\n",
    "    model.add(Activation('softmax'))\n",
    "\n",
    "    return model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:04:54.758948Z",
     "start_time": "2019-04-11T07:04:53.581996Z"
    }
   },
   "outputs": [],
   "source": [
    "model = createModel()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:04:54.774980Z",
     "start_time": "2019-04-11T07:04:54.762035Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d_1 (Conv2D)            (None, 48, 48, 64)        640       \n",
      "_________________________________________________________________\n",
      "batch_normalization_1 (Batch (None, 48, 48, 64)        256       \n",
      "_________________________________________________________________\n",
      "activation_1 (Activation)    (None, 48, 48, 64)        0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_1 (MaxPooling2 (None, 24, 24, 64)        0         \n",
      "_________________________________________________________________\n",
      "dropout_1 (Dropout)          (None, 24, 24, 64)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_2 (Conv2D)            (None, 24, 24, 128)       73856     \n",
      "_________________________________________________________________\n",
      "batch_normalization_2 (Batch (None, 24, 24, 128)       512       \n",
      "_________________________________________________________________\n",
      "activation_2 (Activation)    (None, 24, 24, 128)       0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_2 (MaxPooling2 (None, 12, 12, 128)       0         \n",
      "_________________________________________________________________\n",
      "dropout_2 (Dropout)          (None, 12, 12, 128)       0         \n",
      "_________________________________________________________________\n",
      "conv2d_3 (Conv2D)            (None, 12, 12, 256)       295168    \n",
      "_________________________________________________________________\n",
      "batch_normalization_3 (Batch (None, 12, 12, 256)       1024      \n",
      "_________________________________________________________________\n",
      "activation_3 (Activation)    (None, 12, 12, 256)       0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_3 (MaxPooling2 (None, 6, 6, 256)         0         \n",
      "_________________________________________________________________\n",
      "dropout_3 (Dropout)          (None, 6, 6, 256)         0         \n",
      "_________________________________________________________________\n",
      "conv2d_4 (Conv2D)            (None, 6, 6, 512)         1180160   \n",
      "_________________________________________________________________\n",
      "batch_normalization_4 (Batch (None, 6, 6, 512)         2048      \n",
      "_________________________________________________________________\n",
      "activation_4 (Activation)    (None, 6, 6, 512)         0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_4 (MaxPooling2 (None, 3, 3, 512)         0         \n",
      "_________________________________________________________________\n",
      "dropout_4 (Dropout)          (None, 3, 3, 512)         0         \n",
      "_________________________________________________________________\n",
      "flatten_1 (Flatten)          (None, 4608)              0         \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 512)               2359808   \n",
      "_________________________________________________________________\n",
      "batch_normalization_5 (Batch (None, 512)               2048      \n",
      "_________________________________________________________________\n",
      "activation_5 (Activation)    (None, 512)               0         \n",
      "_________________________________________________________________\n",
      "dropout_5 (Dropout)          (None, 512)               0         \n",
      "_________________________________________________________________\n",
      "dense_2 (Dense)              (None, 256)               131328    \n",
      "_________________________________________________________________\n",
      "batch_normalization_6 (Batch (None, 256)               1024      \n",
      "_________________________________________________________________\n",
      "activation_6 (Activation)    (None, 256)               0         \n",
      "_________________________________________________________________\n",
      "dropout_6 (Dropout)          (None, 256)               0         \n",
      "_________________________________________________________________\n",
      "dense_3 (Dense)              (None, 7)                 1799      \n",
      "_________________________________________________________________\n",
      "activation_7 (Activation)    (None, 7)                 0         \n",
      "=================================================================\n",
      "Total params: 4,049,671\n",
      "Trainable params: 4,046,215\n",
      "Non-trainable params: 3,456\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And visualize the model architecture :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:04:58.998055Z",
     "start_time": "2019-04-11T07:04:58.776767Z"
    }
   },
   "outputs": [],
   "source": [
    "plot_model(model, to_file='model_images/model_plot.png', show_shapes=True, show_layer_names=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src='model_images/model_plot.png'>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To prevent overfitting, we can do data augmentation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-04-11T07:05:04.244323Z",
     "start_time": "2019-04-11T07:05:04.239916Z"
    }
   },
   "outputs": [],
   "source": [
    "datagen = ImageDataGenerator(\n",
    "        zoom_range=0.2,          # randomly zoom into images\n",
    "        rotation_range=10,       # randomly rotate images in the range (degrees, 0 to 180)\n",
    "        width_shift_range=0.1,   # randomly shift images horizontally (fraction of total width)\n",
    "        height_shift_range=0.1,  # randomly shift images vertically (fraction of total height)\n",
    "        horizontal_flip=True,    # randomly flip images\n",
    "        vertical_flip=False)     # randomly flip images"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-06T08:11:17.011434Z",
     "start_time": "2019-03-06T08:11:16.655357Z"
    }
   },
   "outputs": [],
   "source": [
    "# augment and fit data\n",
    "datagen = ImageDataGenerator(\n",
    "    featurewise_center=False,\n",
    "    samplewise_center=False,\n",
    "    featurewise_std_normalization=False,\n",
    "    samplewise_std_normalization=False,\n",
    "    zca_whitening=False,\n",
    "    rotation_range=0,\n",
    "    width_shift_range=0.0,\n",
    "    height_shift_range=0.0,\n",
    "    shear_range=0,\n",
    "    zoom_range=0.0,\n",
    "    horizontal_flip=True,\n",
    "    vertical_flip=False)\n",
    "datagen.fit(X_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2019-04-11T07:05:13.449Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/50\n"
     ]
    }
   ],
   "source": [
    "#Creating 2nd model and training(fitting)\n",
    "\n",
    "model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])\n",
    " \n",
    "batch_size = 128\n",
    "epochs = 50\n",
    "\n",
    "# Fit the model on the batches generated by datagen.flow().\n",
    "history = model.fit_generator(\n",
    "    datagen.flow(X_train, y_train, batch_size=batch_size),\n",
    "    steps_per_epoch=int(np.ceil(X_train.shape[0] / float(batch_size))),\n",
    "    epochs = epochs, \n",
    "    validation_data=(X_test, y_test), class_weight=class_weight)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# V. Evaluate the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-14T10:58:20.919730Z",
     "start_time": "2019-03-14T10:58:20.568843Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'history' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-6-e059f9b557ac>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0;31m# Loss Curves\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfigsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m8\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhistory\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhistory\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'loss'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'r'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mlinewidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2.0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      6\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhistory\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhistory\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'val_loss'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'b'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mlinewidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2.0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlegend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'Training loss'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'Validation Loss'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mfontsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m18\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mNameError\u001b[0m: name 'history' is not defined"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 576x432 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#Plotting accuracy and loss curves for 2nd model\n",
    "\n",
    "# Loss Curves\n",
    "plt.figure(figsize=[8,6])\n",
    "plt.plot(history.history['loss'],'r',linewidth=2.0)\n",
    "plt.plot(history.history['val_loss'],'b',linewidth=2.0)\n",
    "plt.legend(['Training loss', 'Validation Loss'],fontsize=18)\n",
    "plt.xlabel('Epochs ',fontsize=16)\n",
    "plt.ylabel('Loss',fontsize=16)\n",
    "plt.title('Loss Curves',fontsize=16)\n",
    " \n",
    "# Accuracy Curves\n",
    "plt.figure(figsize=[8,6])\n",
    "plt.plot(history.history['acc'],'r',linewidth=2.0)\n",
    "plt.plot(history.history['val_acc'],'b',linewidth=2.0)\n",
    "plt.legend(['Training Accuracy', 'Validation Accuracy'],fontsize=18)\n",
    "plt.xlabel('Epochs ',fontsize=16)\n",
    "plt.ylabel('Accuracy',fontsize=16)\n",
    "plt.title('Accuracy Curves',fontsize=16)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# VI. Feed the gradient image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-14T10:59:06.547732Z",
     "start_time": "2019-03-14T10:59:06.147401Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x1a3c7bce48>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "im = X_train[0].reshape(48,48)\n",
    "plt.imshow(im, 'gray')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-14T10:59:08.987024Z",
     "start_time": "2019-03-14T10:59:08.973226Z"
    }
   },
   "outputs": [],
   "source": [
    "gx = cv2.Sobel(im, cv2.CV_32F, 1, 0, ksize=1)\n",
    "gy = cv2.Sobel(im, cv2.CV_32F, 0, 1, ksize=1)\n",
    "mag, angle = cv2.cartToPolar(gx, gy, angleInDegrees=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-14T10:59:37.708498Z",
     "start_time": "2019-03-14T10:59:37.308800Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 864x576 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(12,8))\n",
    "plt.imshow(mag, 'gray')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# VII. Save the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-07T07:36:40.933059Z",
     "start_time": "2019-03-07T07:36:38.628807Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "9521"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#save the model weights\n",
    "json_string = model.to_json()\n",
    "model.save_weights(local_path + 'savedmodels/model_simple_2.h5')\n",
    "open(local_path + 'savedmodels/model_simple_2.json', 'w').write(json_string)\n",
    "#model.save_weights(local_path + 'savedmodels/Emotion_Face_Detection_Model.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-07T07:36:45.396761Z",
     "start_time": "2019-03-07T07:36:42.688716Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loaded model from disk\n"
     ]
    }
   ],
   "source": [
    "with open(local_path + 'savedmodels/model_simple_2.json','r') as f:\n",
    "    json = f.read()\n",
    "model = model_from_json(json)\n",
    "\n",
    "model.load_weights(local_path + 'savedmodels/model_simple_2.h5')\n",
    "print(\"Loaded model from disk\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# VIII. Sources"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Visualization : https://github.com/JostineHo/mememoji/blob/master/data_visualization.ipynb\n",
    "- State of the art Architecture : https://github.com/amineHorseman/facial-expression-recognition-using-cnn\n",
    "- Eyes Tracking : https://www.pyimagesearch.com/2017/04/24/eye-blink-detection-opencv-python-dlib/\n",
    "- Face Alignment : https://www.pyimagesearch.com/2017/05/22/face-alignment-with-opencv-and-python/\n",
    "- C.Pramerdorfer,  and  M.Kampel.Facial  Expression  Recognition  using  Con-volutional  Neural  Networks:  State  of  the  Art.  Computer  Vision  Lab,  TU  Wien. https://arxiv.org/pdf/1612.02903.pdf\n",
    "- A Brief Review of Facial Emotion Recognition Based\n",
    "on Visual Information : https://www.mdpi.com/1424-8220/18/2/401/pdf\n",
    "- Going deeper in facial expression recognition using deep neural networks : https://ieeexplore.ieee.org/document/7477450"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autoclose": false,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": false,
   "user_envs_cfg": false
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
